package c

/*
#cgo linux LDFLAGS: -ldl
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef _WIN32
#include <windows.h>
#ifdef _WIN64
	typedef unsigned __int64  uintptr_t;
#else
	typedef unsigned int uintptr_t;
#endif
static uintptr_t loadLib(const char* fn) {
    return (uintptr_t)LoadLibraryA(fn);
}

static void freeLib(uintptr_t handle) {
    FreeLibrary((HMODULE)handle);
}

static void* getProc(uintptr_t handle, const char* name) {
    return (void*)GetProcAddress((HMODULE)handle, name);
}
#else
#include <dlfcn.h>
#include <limits.h>
static uintptr_t loadLib(const char* fn) {
    return (uintptr_t)dlopen(fn,RTLD_NOW|RTLD_GLOBAL);
}

static void freeLib(uintptr_t handle) {
    dlclose((void*)handle);
}

static void* getProc(uintptr_t handle, const char* name) {
    return (void*)dlsym((void*)handle, name);
}
#endif

*/
import "C"
import (
	"unsafe"
)

type Library struct {
	libpath string
	h       uintptr
	//err     string // set if failed to load
	//loaded     chan struct{} // closed when loaded
	syms map[string]unsafe.Pointer
}

func OpenLibrary(name string) *Library {
	cPath := make([]byte, len(name)+1)
	copy(cPath, name)
	h := C.loadLib((*C.char)(unsafe.Pointer(&cPath[0])))
	if h != 0 {
		p := new(Library)
		p.libpath = name
		p.h = uintptr(h)
		p.syms = make(map[string]unsafe.Pointer)
		return p
	}
	return nil
}

func CloseLibrary(p *Library) {
	C.freeLib(C.uintptr_t(p.h))
}

func (p *Library) Get(name string) unsafe.Pointer {
	fun, ok := p.syms[name]
	if !ok {
		cName := make([]byte, len(name)+1)
		copy(cName, name)
		f := C.getProc(C.uintptr_t(p.h), (*C.char)(unsafe.Pointer(&cName[0])))
		if f != nil {
			fun = unsafe.Pointer(f)
			p.syms[name] = fun
		}
	}
	return fun
}
